﻿#region 참조 네임스페이스

using Skying.Management;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Management;
using System.Runtime.InteropServices;
using System.Threading;

#endregion

namespace Skying
{
	/// <summary>
	/// <see cref="System.OperatingSystem"/> 클래스의 확장 메서드를 제공한다.
	/// </summary>
	public static class OperatingSystemExtensions
	{
		/// <summary>
		/// <para>활성화되어 있는 사용자를 로그오프시키거나, 시스템을 셧다운시키거나, 시스템을 다시 시작시킨다.</para>
		/// <para>이 메서드는 모든 어플리케이션들이 제거될 수 있는지를 결정하도록 WM_QUERYENDSESSION 메시지를 보낸다.</para>
		/// </summary>
		/// <param name="exitWindows">셧다운 유형을 결정한다.</param>
		/// <param name="reason">셧다운하는 이유를 결정한다.</param>
		/// <returns>
		/// <para>메서드가 성공적으로 실행되면 true, 그렇지 않으면 false가 반환된다.</para>
		/// <para>이 메서드는 비동기적으로 실행되기 때문에 true가 셧다운이 성공했음을 나타내지는 않는다.</para>
		/// </returns>
		/// <remarks>
		/// 더 자세한 정보는 http://msdn.microsoft.com/en-us/library/windows/desktop/aa376868(v=vs.85).aspx 를 참조하시오.
		/// </remarks>
		[DllImport("user32.dll", SetLastError = true)]
		[return: MarshalAs(UnmanagedType.Bool)]
		static extern bool ExitWindowsEx(ExitWindows exitWindows, ShutdownReason reason);

		/// <summary>
		/// <para>운영체제로 Win32Shutdown 명령을 요청한다. 이 명령으로 운영체제를 logoff, shutdown, reboot할 수 있다.</para>
		/// <para>shutdown, poweroff 명령을 요청하는 경우, 열려있는 세션(로그인된 사용자의 세션)에서 사용하고 있는 프로세스들을 모두 강제적으로 종료시킨다.</para>
		/// </summary>
		/// <param name="environment"><see cref="System.OperatingSystem"/> 클래스의 메서드 확장을 위한 파라메터로 무시한다.</param>
		/// <param name="exitWindows">셧다운 유형을 설정한다.</param>
		public static void Shutdown(this OperatingSystem environment, ExitWindows exitWindows)
		{
			// SeShutdownPrivilege 명령에 대한 권한을 활성화시킨다.
			if (TokenAdjuster.SetPrivilege("SeShutdownPrivilege", true))
			{
				Thread thread = new Thread(new ThreadStart(() =>
				{
					// reboot, poweroff, shutdown에 관련된 명령일 때만 세션 프로세스들을 종료시킨다.
					switch (exitWindows)
					{
						case ExitWindows.Reboot:
						case ExitWindows.PowerOff:
						case ExitWindows.Shutdown:

							ObjectQuery query = new ObjectQuery("select * from Win32_SessionProcess");

							ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);

							ManagementObjectCollection processObjects = searcher.Get();

							IList<int> sessionProcessHandles = new List<int>();

							// 활성 세션 프로세스들의 핸들 값을 수집한다.
							foreach (ManagementObject processObject in processObjects)
							{
								string dependent = processObject["Dependent"] as string;

								int handle = -1;

								if (int.TryParse(dependent.Substring(dependent.IndexOf("\"")).Replace("\"", string.Empty), out handle))
								{
									sessionProcessHandles.Add(handle);
								}
							}

							Process[] processes = Process.GetProcesses();
							Process currentProcess = Process.GetCurrentProcess();

							// 모든 프로세스들 중 현재 프로세스(이 메서드를 실행한 프로세스)를 제외한 세션 프로세스들을 모두 종료시킨다.
							foreach (Process process in processes)
							{
								if (!currentProcess.Id.Equals(process.Id) && sessionProcessHandles.Contains(process.Id))
								{
									process.Kill();
								}
							}

							break;
					}

					ExitWindowsEx(exitWindows, ShutdownReason.MajorSoftware);
				}));

				thread.SetApartmentState(ApartmentState.MTA);

				thread.Start();
			}
		}
	}

	/// <summary>
	/// Win32Shutdown 메서드에 대한 동작 유형을 열거한다.
	/// </summary>
	/// <remarks>
	/// 더 자세한 정보는 http://msdn.microsoft.com/en-us/library/windows/desktop/aa376868(v=vs.85).aspx 를 참조하시오.
	/// </remarks>
	[Flags]
	public enum ExitWindows : uint
	{
		HybridShutdown = 0x004,
		LogOff = 0x00,
		Shutdown = 0x01,
		Reboot = 0x02,
		PowerOff = 0x08,
		RestartApps = 0x40,
		Force = 0x04,
		ForceIfHung = 0x10,
	}

	/// <summary>
	/// 셧다운하는 이유에 대한 열거형을 제공한다.
	/// </summary>
	/// <remarks>
	/// 더 자세한 정보는 http://msdn.microsoft.com/en-us/library/windows/desktop/aa376885(v=vs.85).aspx 를 참조하시오.
	/// </remarks>
	[Flags]
	public enum ShutdownReason : uint
	{
		MajorApplication = 0x00040000,
		MajorHardware = 0x00010000,
		MajorLegacyApi = 0x00070000,
		MajorOperatingSystem = 0x00020000,
		MajorOther = 0x00000000,
		MajorPower = 0x00060000,
		MajorSoftware = 0x00030000,
		MajorSystem = 0x00050000,

		MinorBlueScreen = 0x0000000F,
		MinorCordUnplugged = 0x0000000b,
		MinorDisk = 0x00000007,
		MinorEnvironment = 0x0000000c,
		MinorHardwareDriver = 0x0000000d,
		MinorHotfix = 0x00000011,
		MinorHung = 0x00000005,
		MinorInstallation = 0x00000002,
		MinorMaintenance = 0x00000001,
		MinorMMC = 0x00000019,
		MinorNetworkConnectivity = 0x00000014,
		MinorNetworkCard = 0x00000009,
		MinorOther = 0x00000000,
		MinorOtherDriver = 0x0000000e,
		MinorPowerSupply = 0x0000000a,
		MinorProcessor = 0x00000008,
		MinorReconfig = 0x00000004,
		MinorSecurity = 0x00000013,
		MinorSecurityFix = 0x00000012,
		MinorSecurityFixUninstall = 0x00000018,
		MinorServicePack = 0x00000010,
		MinorServicePackUninstall = 0x00000016,
		MinorTermSrv = 0x00000020,
		MinorUnstable = 0x00000006,
		MinorUpgrade = 0x00000003,
		MinorWMI = 0x00000015,

		FlagUserDefined = 0x40000000,
		FlagPlanned = 0x80000000
	}
}